package keel.Algorithms.ImbalancedClassification.Resampling.SMOTE_RSB.Rough_Sets;

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.Serializable;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.text.ParseException;
import java.util.Enumeration;
import java.util.Random;
import java.util.Vector;

/**
 * Class for handling an ordered set of weighted instances.
 * <p>
 * 
 * Typical usage (code from the main() method of this class):
 * <p>
 * 
 * <code>
 * ... <br>
 * 
 * // Read all the instances in the file <br>
 * reader = new FileReader(filename); <br>
 * instances = new Instances(reader); <br><br>
 *
 * // Make the last attribute be the class <br>
 * instances.setClassIndex(instances.numAttributes() - 1); <br><br>
 * 
 * // Print header and instances. <br>
 * System.out.println("\nDataset:\n"); <br> 
 * System.out.println(instances); <br><br>
 *
 * ... <br>
 * </code>
 * <p>
 * 
 * All methods that change a set of instances are safe, ie. a change of a set of
 * instances does not affect any other sets of instances. All methods that
 * change a datasets's attribute information clone the dataset before it is
 * changed.
 * 
 * @author Eibe Frank (eibe@cs.waikato.ac.nz)
 * @author Len Trigg (trigg@cs.waikato.ac.nz)
 * @version $Revision: 1.1 $
 */
public class Instances implements Serializable {

	/** The filename extension that should be used for arff files */
	public static String FILE_EXTENSION = ".arff";

	/**
	 * The filename extension that should be used for bin. serialized instances
	 * files
	 */
	public static String SERIALIZED_OBJ_FILE_EXTENSION = ".bsi";

	/** The keyword used to denote the start of an arff header */
	static String ARFF_RELATION = "@relation";

	/** The keyword used to denote the start of the arff data section */
	static String ARFF_DATA = "@data";

	/** The dataset's name. */
	public/* @spec_public non_null@ */String m_RelationName;

	/** The attribute information. */
	protected/* @spec_public non_null@ */FastVector m_Attributes;
	/*
	 * public invariant (\forall int i; 0 <= i && i < m_Attributes.size();
	 * m_Attributes.elementAt(i) != null);
	 */

	/** The instances. */
	protected/* @spec_public non_null@ */FastVector m_Instances;

	/** The class attribute's index */
	protected int m_ClassIndex;
	// @ protected invariant classIndex() == m_ClassIndex;

	/** Buffer of values for sparse instance */
	protected double[] m_ValueBuffer;

	/** Buffer of indices for sparse instance */
	protected int[] m_IndicesBuffer;

	/**
	 * Reads an ARFF file from a reader, and assigns a weight of one to each
	 * instance. Lets the index of the class attribute be undefined (negative).
	 * 
	 * @param reader
	 *            the reader
	 * @exception IOException
	 *                if the ARFF file is not read successfully
	 */
	public Instances(/* @non_null@ */Reader reader) throws IOException {

		StreamTokenizer tokenizer;

		tokenizer = new StreamTokenizer(reader);
		initTokenizer(tokenizer);
		readHeader(tokenizer);
		m_ClassIndex = -1;
		m_Instances = new FastVector(1000);
		while (getInstance(tokenizer, true)) {};
		compactify();
		setClassIndex(numAttributes() - 1);
	}
	
	public Instances(Vector<Instance> datasets){
		
	}

	/**
	 * Reads the header of an ARFF file from a reader and reserves space for the
	 * given number of instances. Lets the class index be undefined (negative).
	 * 
	 * @param reader
	 *            the reader
	 * @param capacity
	 *            the capacity
	 * @exception IllegalArgumentException
	 *                if the header is not read successfully or the capacity is
	 *                negative.
	 * @exception IOException
	 *                if there is a problem with the reader.
	 */
	// @ requires capacity >= 0;
	// @ ensures classIndex() == -1;
	public Instances(/* @non_null@ */Reader reader, int capacity)
			throws IOException {

		StreamTokenizer tokenizer;

		if (capacity < 0) {
			throw new IllegalArgumentException("Capacity has to be positive!");
		}
		tokenizer = new StreamTokenizer(reader);
		initTokenizer(tokenizer);
		readHeader(tokenizer);
		m_ClassIndex = -1;
		m_Instances = new FastVector(capacity);
	}

	/**
	 * Constructor copying all instances and references to the header
	 * information from the given set of instances.
	 * 
	 * @param dataset
	 *            the set to be copied
	 */
	public Instances(/* @non_null@ */Instances dataset) {

		this(dataset, dataset.numInstances());

		dataset.copyInstances(0, this, dataset.numInstances());
	}

	/**
	 * Constructor creating an empty set of instances. Copies references to the
	 * header information from the given set of instances. Sets the capacity of
	 * the set of instances to 0 if its negative.
	 * 
	 * @param dataset
	 *            the instances from which the header information is to be taken
	 * @param capacity
	 *            the capacity of the new dataset
	 */
	public Instances(/* @non_null@ */Instances dataset, int capacity) {

		if (capacity < 0) {
			capacity = 0;
		}

		// Strings only have to be "shallow" copied because
		// they can't be modified.
		m_ClassIndex = dataset.m_ClassIndex;
		m_RelationName = dataset.m_RelationName;
		m_Attributes = dataset.m_Attributes;
		m_Instances = new FastVector(capacity);
	}
	
	public Instances(String relation, FastVector instances, FastVector attributes, int classIndex ){
		m_RelationName = relation;
		m_ClassIndex = classIndex;
		m_Attributes = attributes;
		m_Instances = (FastVector)instances.copy();
	}

	/**
	 * Creates a new set of instances by copying a subset of another set.
	 * 
	 * @param source
	 *            the set of instances from which a subset is to be created
	 * @param first
	 *            the index of the first instance to be copied
	 * @param toCopy
	 *            the number of instances to be copied
	 * @exception IllegalArgumentException
	 *                if first and toCopy are out of range
	 */
	// @ requires 0 <= first;
	// @ requires 0 <= toCopy;
	// @ requires first + toCopy <= source.numInstances();
	public Instances(/* @non_null@ */Instances source, int first, int toCopy) {

		this(source, toCopy);

		if ((first < 0) || ((first + toCopy) > source.numInstances())) {
			throw new IllegalArgumentException(
					"Parameters first and/or toCopy out " + "of range");
		}
		source.copyInstances(first, this, toCopy);
	}

	/**
	 * Creates an empty set of instances. Uses the given attribute information.
	 * Sets the capacity of the set of instances to 0 if its negative. Given
	 * attribute information must not be changed after this constructor has been
	 * used.
	 * 
	 * @param name
	 *            the name of the relation
	 * @param attInfo
	 *            the attribute information
	 * @param capacity
	 *            the capacity of the set
	 */
	public Instances(/* @non_null@ */String name,
	/* @non_null@ */FastVector attInfo, int capacity) {

		m_RelationName = name;
		m_ClassIndex = -1;
		m_Attributes = attInfo;
		for (int i = 0; i < numAttributes(); i++) {
			attribute(i).setIndex(i);
		}
		m_Instances = new FastVector(capacity);
	}

	/**
	 * Create a copy of the structure, but "cleanse" string types (i.e. doesn't
	 * contain references to the strings seen in the past). Also cleanses all
	 * relational attributes.
	 * 
	 * @return a copy of the instance structure.
	 */
	public Instances stringFreeStructure() {

		FastVector atts = (FastVector) m_Attributes.copy();
		for (int i = 0; i < atts.size(); i++) {
			Attribute att = (Attribute) atts.elementAt(i);
			if (att.type() == Attribute.STRING) {
				atts.setElementAt(new Attribute(att.name(), (FastVector) null),
						i);
			} else if (att.type() == Attribute.RELATIONAL) {
				atts.setElementAt(new Attribute(att.name(), att.relation()), i);
			}
		}
		Instances result = new Instances(relationName(), atts, 0);
		result.m_ClassIndex = m_ClassIndex;
		return result;
	}
	
	public FastVector getAtributtes(){
		return this.m_Attributes;
	}

	/**
	 * Adds one instance to the end of the set. Shallow copies instance before
	 * it is added. Increases the size of the dataset if it is not large enough.
	 * Does not check if the instance is compatible with the dataset.
	 * 
	 * @param instance
	 *            the instance to be added
	 */
	public void add(/* @non_null@ */Instance instance) {

		Instance newInstance = (Instance) instance.copy();

		newInstance.setDataset(this);
		m_Instances.addElement(newInstance);
	}

	/**
	 * Returns an attribute.
	 * 
	 * @param index
	 *            the attribute's index
	 * @return the attribute at the given position
	 */
	// @ requires 0 <= index;
	// @ requires index < m_Attributes.size();
	// @ ensures \result != null;
	public/* @pure@ */Attribute attribute(int index) {

		return (Attribute) m_Attributes.elementAt(index);
	}

	/**
	 * Returns an attribute given its name. If there is more than one attribute
	 * with the same name, it returns the first one. Returns null if the
	 * attribute can't be found.
	 * 
	 * @param name
	 *            the attribute's name
	 * @return the attribute with the given name, null if the attribute can't be
	 *         found
	 */
	public/* @pure@ */Attribute attribute(String name) {

		for (int i = 0; i < numAttributes(); i++) {
			if (attribute(i).name().equals(name)) {
				return attribute(i);
			}
		}
		return null;
	}

	/**
	 * Checks for string attributes in the dataset
	 * 
	 * @return true if string attributes are present, false otherwise
	 */
	public/* @pure@ */boolean checkForStringAttributes() {

		int i = 0;

		while (i < m_Attributes.size()) {
			if (attribute(i++).isString()) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Checks if the given instance is compatible with this dataset. Only looks
	 * at the size of the instance and the ranges of the values for nominal and
	 * string attributes.
	 * 
	 * @return true if the instance is compatible with the dataset
	 */
	public/* @pure@ */boolean checkInstance(Instance instance) {

		if (instance.numAttributes() != numAttributes()) {
			return false;
		}
		for (int i = 0; i < numAttributes(); i++) {
			if (instance.isMissing(i)) {
				continue;
			} else if (attribute(i).isNominal() || attribute(i).isString()) {
				if (!(Utils.eq(instance.value(i), (double) (int) instance
						.value(i)))) {
					return false;
				} else if (Utils.sm(instance.value(i), 0)
						|| Utils
								.gr(instance.value(i), attribute(i).numValues())) {
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * Returns the class attribute.
	 * 
	 * @return the class attribute
	 * @exception UnassignedClassException
	 *                if the class is not set
	 */
	// @ requires classIndex() >= 0;
	public/* @pure@ */Attribute classAttribute() {

		if (m_ClassIndex < 0) {
			throw new UnassignedClassException(
					"Class index is negative (not set)!");
		}
		return attribute(m_ClassIndex);
	}

	/**
	 * Returns the class attribute's index. Returns negative number if it's
	 * undefined.
	 * 
	 * @return the class index as an integer
	 */
	// ensures \result == m_ClassIndex;
	public/* @pure@ */int classIndex() {

		return m_ClassIndex;
	}

	/**
	 * Compactifies the set of instances. Decreases the capacity of the set so
	 * that it matches the number of instances in the set.
	 */
	public void compactify() {

		m_Instances.trimToSize();
	}

	/**
	 * Removes all instances from the set.
	 */
	public void delete() {

		m_Instances = new FastVector();
	}

	/**
	 * Removes an instance at the given position from the set.
	 * 
	 * @param index
	 *            the instance's position
	 */
	// @ requires 0 <= index && index < numInstances();
	public void delete(int index) {

		m_Instances.removeElementAt(index);
	}

	/**
	 * Deletes an attribute at the given position (0 to numAttributes() - 1). A
	 * deep copy of the attribute information is performed before the attribute
	 * is deleted.
	 * 
	 * @param position
	 *            the attribute's position
	 * @exception IllegalArgumentException
	 *                if the given index is out of range or the class attribute
	 *                is being deleted
	 */
	// @ requires 0 <= position && position < numAttributes();
	// @ requires position != classIndex();
	public void deleteAttributeAt(int position) {

		if ((position < 0) || (position >= m_Attributes.size())) {
			throw new IllegalArgumentException("Index out of range");
		}
		if (position == m_ClassIndex) {
			throw new IllegalArgumentException("Can't delete class attribute");
		}
		freshAttributeInfo();
		if (m_ClassIndex > position) {
			m_ClassIndex--;
		}
		m_Attributes.removeElementAt(position);
		for (int i = position; i < m_Attributes.size(); i++) {
			Attribute current = (Attribute) m_Attributes.elementAt(i);
			current.setIndex(current.index() - 1);
		}
		for (int i = 0; i < numInstances(); i++) {
			instance(i).forceDeleteAttributeAt(position);
		}
	}

	/**
	 * Deletes all string attributes in the dataset. A deep copy of the
	 * attribute information is performed before an attribute is deleted.
	 * 
	 * @exception IllegalArgumentException
	 *                if string attribute couldn't be successfully deleted
	 *                (probably because it is the class attribute).
	 */
	public void deleteStringAttributes() {

		int i = 0;
		while (i < m_Attributes.size()) {
			if (attribute(i).isString()) {
				deleteAttributeAt(i);
			} else {
				i++;
			}
		}
	}

	/**
	 * Removes all instances with missing values for a particular attribute from
	 * the dataset.
	 * 
	 * @param attIndex
	 *            the attribute's index
	 */
	// @ requires 0 <= attIndex && attIndex < numAttributes();
	public void deleteWithMissing(int attIndex) {

		FastVector newInstances = new FastVector(numInstances());

		for (int i = 0; i < numInstances(); i++) {
			if (!instance(i).isMissing(attIndex)) {
				newInstances.addElement(instance(i));
			}
		}
		m_Instances = newInstances;
	}

	/**
	 * Removes all instances with missing values for a particular attribute from
	 * the dataset.
	 * 
	 * @param att
	 *            the attribute
	 */
	public void deleteWithMissing(/* @non_null@ */Attribute att) {

		deleteWithMissing(att.index());
	}

	/**
	 * Removes all instances with a missing class value from the dataset.
	 * 
	 * @exception UnassignedClassException
	 *                if class is not set
	 */
	public void deleteWithMissingClass() {

		if (m_ClassIndex < 0) {
			throw new UnassignedClassException(
					"Class index is negative (not set)!");
		}
		deleteWithMissing(m_ClassIndex);
	}

	/**
	 * Returns an enumeration of all the attributes.
	 * 
	 * @return enumeration of all the attributes.
	 */
	public/* @non_null pure@ */Enumeration enumerateAttributes() {

		return m_Attributes.elements(m_ClassIndex);
	}

	/**
	 * Returns an enumeration of all instances in the dataset.
	 * 
	 * @return enumeration of all instances in the dataset
	 */
	public/* @non_null pure@ */Enumeration enumerateInstances() {

		return m_Instances.elements();
	}

	/**
	 * Checks if two headers are equivalent.
	 * 
	 * @param dataset
	 *            another dataset
	 * @return true if the header of the given dataset is equivalent to this
	 *         header
	 */
	public/* @pure@ */boolean equalHeaders(Instances dataset) {

		// Check class and all attributes
		if (m_ClassIndex != dataset.m_ClassIndex) {
			return false;
		}
		if (m_Attributes.size() != dataset.m_Attributes.size()) {
			return false;
		}
		for (int i = 0; i < m_Attributes.size(); i++) {
			if (!(attribute(i).equals(dataset.attribute(i)))) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Returns the first instance in the set.
	 * 
	 * @return the first instance in the set
	 */
	// @ requires numInstances() > 0;
	public/* @non_null pure@ */Instance firstInstance() {

		return (Instance) m_Instances.firstElement();
	}

	/**
	 * Returns a random number generator. The initial seed of the random number
	 * generator depends on the given seed and the hash code of a string
	 * representation of a instances chosen based on the given seed.
	 * 
	 * @param seed
	 *            the given seed
	 * @return the random number generator
	 */
	public Random getRandomNumberGenerator(long seed) {

		Random r = new Random(seed);
		r.setSeed(instance(r.nextInt(numInstances())).toString().hashCode()
				+ seed);
		return r;
	}

	/**
	 * Inserts an attribute at the given position (0 to numAttributes()) and
	 * sets all values to be missing. Shallow copies the attribute before it is
	 * inserted, and performs a deep copy of the existing attribute information.
	 * 
	 * @param att
	 *            the attribute to be inserted
	 * @param position
	 *            the attribute's position
	 * @exception IllegalArgumentException
	 *                if the given index is out of range
	 */
	// @ requires 0 <= position;
	// @ requires position <= numAttributes();
	public void insertAttributeAt(/* @non_null@ */Attribute att, int position) {

		if ((position < 0) || (position > m_Attributes.size())) {
			throw new IllegalArgumentException("Index out of range");
		}
		att = (Attribute) att.copy();
		freshAttributeInfo();
		att.setIndex(position);
		m_Attributes.insertElementAt(att, position);
		for (int i = position + 1; i < m_Attributes.size(); i++) {
			Attribute current = (Attribute) m_Attributes.elementAt(i);
			current.setIndex(current.index() + 1);
		}
		for (int i = 0; i < numInstances(); i++) {
			instance(i).forceInsertAttributeAt(position);
		}
		if (m_ClassIndex >= position) {
			m_ClassIndex++;
		}
	}

	/**
	 * Returns the instance at the given position.
	 * 
	 * @param index
	 *            the instance's index
	 * @return the instance at the given position
	 */
	// @ requires 0 <= index;
	// @ requires index < numInstances();
	public/* @non_null pure@ */Instance instance(int index) {

		return (Instance) m_Instances.elementAt(index);
	}

	/**
	 * Returns the kth-smallest attribute value of a numeric attribute. Note
	 * that calling this method will change the order of the data!
	 * 
	 * @param att
	 *            the Attribute object
	 * @param k
	 *            the value of k
	 * @return the kth-smallest value
	 */
	public double kthSmallestValue(Attribute att, int k) {

		return kthSmallestValue(att.index(), k);
	}

	/**
	 * Returns the kth-smallest attribute value of a numeric attribute. Note
	 * that calling this method will change the order of the data! The number of
	 * non-missing values in the data must be as least as last as k for this to
	 * work.
	 * 
	 * @param attIndex
	 *            the attribute's index
	 * @param k
	 *            the value of k
	 * @return the kth-smallest value
	 */
	public double kthSmallestValue(int attIndex, int k) {

		if (!attribute(attIndex).isReal()) {
			throw new IllegalArgumentException(
					"Instances: attribute must be real to compute kth-smallest value.");
		}

		int i, j;

		// move all instances with missing values to end
		j = numInstances() - 1;
		i = 0;
		while (i <= j) {
			if (instance(j).isMissing(attIndex)) {
				j--;
			} else {
				if (instance(i).isMissing(attIndex)) {
					swap(i, j);
					j--;
				}
				i++;
			}
		}

		if ((k < 0) || (k > j)) {
			throw new IllegalArgumentException(
					"Instances: value for k for computing kth-smallest value too large.");
		}

		return instance(select(attIndex, 0, j, k)).value(attIndex);
	}

	/**
	 * Returns the last instance in the set.
	 * 
	 * @return the last instance in the set
	 */
	// @ requires numInstances() > 0;
	public/* @non_null pure@ */Instance lastInstance() {

		return (Instance) m_Instances.lastElement();
	}

	/**
	 * Returns the mean (mode) for a numeric (nominal) attribute as a
	 * floating-point value. Returns 0 if the attribute is neither nominal nor
	 * numeric. If all values are missing it returns zero.
	 * 
	 * @param attIndex
	 *            the attribute's index
	 * @return the mean or the mode
	 */
	public/* @pure@ */double meanOrMode(int attIndex) {

		double result, found;
		int[] counts;

		if (attribute(attIndex).isReal()) {
			result = found = 0;
			for (int j = 0; j < numInstances(); j++) {
				if (!instance(j).isMissing(attIndex)) {
					found += instance(j).weight();
					result += instance(j).weight()
							* instance(j).value(attIndex);
				}
			}
			if (found <= 0) {
				return 0;
			} else {
				return result / found;
			}
		} else if (attribute(attIndex).isNominal()) {
			counts = new int[attribute(attIndex).numValues()];
			for (int j = 0; j < numInstances(); j++) {
				if (!instance(j).isMissing(attIndex)) {
					counts[(int) instance(j).value(attIndex)] += instance(j)
							.weight();
				}
			}
			return (double) Utils.maxIndex(counts);
		} else {
			return 0;
		}
	}

	/**
	 * Returns the mean (mode) for a numeric (nominal) attribute as a
	 * floating-point value. Returns 0 if the attribute is neither nominal nor
	 * numeric. If all values are missing it returns zero.
	 * 
	 * @param att
	 *            the attribute
	 * @return the mean or the mode
	 */
	public/* @pure@ */double meanOrMode(Attribute att) {

		return meanOrMode(att.index());
	}

	/**
	 * Returns the number of attributes.
	 * 
	 * @return the number of attributes as an integer
	 */
	// @ ensures \result == m_Attributes.size();
	public/* @pure@ */int numAttributes() {

		return m_Attributes.size();
	}

	/**
	 * Returns the number of class labels.
	 * 
	 * @return the number of class labels as an integer if the class attribute
	 *         is nominal, 1 otherwise.
	 * @exception UnassignedClassException
	 *                if the class is not set
	 */
	// @ requires classIndex() >= 0;
	public/* @pure@ */int numClasses() {

		if (m_ClassIndex < 0) {
			throw new UnassignedClassException(
					"Class index is negative (not set)!" + m_ClassIndex);
		}
		if (!classAttribute().isNominal()) {
			return 1;
		} else {
			return classAttribute().numValues();
		}
	}

	/**
	 * Returns the number of distinct values of a given attribute. Returns the
	 * number of instances if the attribute is a string attribute. The value
	 * 'missing' is not counted.
	 * 
	 * @param attIndex
	 *            the attribute
	 * @return the number of distinct values of a given attribute
	 */
	// @ requires 0 <= attIndex;
	// @ requires attIndex < numAttributes();
	public/* @pure@ */int numDistinctValues(int attIndex) {

		if (attribute(attIndex).isReal()) {
			double[] attVals = attributeToDoubleArray(attIndex);
			int[] sorted = Utils.sort(attVals);
			double prev = 0;
			int counter = 0;
			for (int i = 0; i < sorted.length; i++) {
				Instance current = instance(sorted[i]);
				if (current.isMissing(attIndex)) {
					break;
				}
				if ((i == 0) || (current.value(attIndex) > prev)) {
					prev = current.value(attIndex);
					counter++;
				}
			}
			return counter;
		} else {
			return attribute(attIndex).numValues();
		}
	}

	/**
	 * Returns the number of distinct values of a given attribute. Returns the
	 * number of instances if the attribute is a string attribute. The value
	 * 'missing' is not counted.
	 * 
	 * @param att
	 *            the attribute
	 * @return the number of distinct values of a given attribute
	 */
	public/* @pure@ */int numDistinctValues(/* @non_null@ */Attribute att) {

		return numDistinctValues(att.index());
	}

	/**
	 * Returns the number of instances in the dataset.
	 * 
	 * @return the number of instances in the dataset as an integer
	 */
	// @ ensures \result == m_Instances.size();
	public/* @pure@ */int numInstances() {

		return m_Instances.size();
	}

	/**
	 * Shuffles the instances in the set so that they are ordered randomly.
	 * 
	 * @param random
	 *            a random number generator
	 */
	public void randomize(Random random) {

		for (int j = numInstances() - 1; j > 0; j--)
			swap(j, random.nextInt(j + 1));
	}

	/**
	 * Reads a single instance from the reader and appends it to the dataset.
	 * Automatically expands the dataset if it is not large enough to hold the
	 * instance. This method does not check for carriage return at the end of
	 * the line.
	 * 
	 * @param reader
	 *            the reader
	 * @return false if end of file has been reached
	 * @exception IOException
	 *                if the information is not read successfully
	 */
	public boolean readInstance(Reader reader) throws IOException {

		StreamTokenizer tokenizer = new StreamTokenizer(reader);

		initTokenizer(tokenizer);
		return getInstance(tokenizer, false);
	}

	/**
	 * Returns the relation's name.
	 * 
	 * @return the relation's name as a string
	 */
	// @ ensures \result == m_RelationName;
	public/* @pure@ */String relationName() {

		return m_RelationName;
	}

	/**
	 * Renames an attribute. This change only affects this dataset.
	 * 
	 * @param att
	 *            the attribute's index
	 * @param name
	 *            the new name
	 */
	public void renameAttribute(int att, String name) {

		Attribute newAtt = attribute(att).copy(name);
		FastVector newVec = new FastVector(numAttributes());

		for (int i = 0; i < numAttributes(); i++) {
			if (i == att) {
				newVec.addElement(newAtt);
			} else {
				newVec.addElement(attribute(i));
			}
		}
		m_Attributes = newVec;
	}

	/**
	 * Renames an attribute. This change only affects this dataset.
	 * 
	 * @param att
	 *            the attribute
	 * @param name
	 *            the new name
	 */
	public void renameAttribute(Attribute att, String name) {

		renameAttribute(att.index(), name);
	}

	/**
	 * Renames the value of a nominal (or string) attribute value. This change
	 * only affects this dataset.
	 * 
	 * @param att
	 *            the attribute's index
	 * @param val
	 *            the value's index
	 * @param name
	 *            the new name
	 */
	public void renameAttributeValue(int att, int val, String name) {

		Attribute newAtt = (Attribute) attribute(att).copy();
		FastVector newVec = new FastVector(numAttributes());

		newAtt.setValue(val, name);
		for (int i = 0; i < numAttributes(); i++) {
			if (i == att) {
				newVec.addElement(newAtt);
			} else {
				newVec.addElement(attribute(i));
			}
		}
		m_Attributes = newVec;
	}

	/**
	 * Renames the value of a nominal (or string) attribute value. This change
	 * only affects this dataset.
	 * 
	 * @param att
	 *            the attribute
	 * @param val
	 *            the value
	 * @param name
	 *            the new name
	 */
	public void renameAttributeValue(Attribute att, String val, String name) {

		int v = att.indexOfValue(val);
		if (v == -1)
			throw new IllegalArgumentException(val + " not found");
		renameAttributeValue(att.index(), v, name);
	}

	/**
	 * Creates a new dataset of the same size using random sampling with
	 * replacement.
	 * 
	 * @param random
	 *            a random number generator
	 * @return the new dataset
	 */
	public Instances resample(Random random) {

		Instances newData = new Instances(this, numInstances());
		while (newData.numInstances() < numInstances()) {
			newData.add(instance(random.nextInt(numInstances())));
		}
		return newData;
	}

	/**
	 * Creates a new dataset of the same size using random sampling with
	 * replacement according to the current instance weights. The weights of the
	 * instances in the new dataset are set to one.
	 * 
	 * @param random
	 *            a random number generator
	 * @return the new dataset
	 */
	public Instances resampleWithWeights(Random random) {

		double[] weights = new double[numInstances()];
		for (int i = 0; i < weights.length; i++) {
			weights[i] = instance(i).weight();
		}
		return resampleWithWeights(random, weights);
	}

	/**
	 * Creates a new dataset of the same size using random sampling with
	 * replacement according to the given weight vector. The weights of the
	 * instances in the new dataset are set to one. The length of the weight
	 * vector has to be the same as the number of instances in the dataset, and
	 * all weights have to be positive.
	 * 
	 * @param random
	 *            a random number generator
	 * @param weights
	 *            the weight vector
	 * @return the new dataset
	 * @exception IllegalArgumentException
	 *                if the weights array is of the wrong length or contains
	 *                negative weights.
	 */
	public Instances resampleWithWeights(Random random, double[] weights) {

		if (weights.length != numInstances()) {
			throw new IllegalArgumentException(
					"weights.length != numInstances.");
		}
		Instances newData = new Instances(this, numInstances());
		if (numInstances() == 0) {
			return newData;
		}
		double[] probabilities = new double[numInstances()];
		double sumProbs = 0, sumOfWeights = Utils.sum(weights);
		for (int i = 0; i < numInstances(); i++) {
			sumProbs += random.nextDouble();
			probabilities[i] = sumProbs;
		}
		Utils.normalize(probabilities, sumProbs / sumOfWeights);

		// Make sure that rounding errors don't mess things up
		probabilities[numInstances() - 1] = sumOfWeights;
		int k = 0;
		int l = 0;
		sumProbs = 0;
		while ((k < numInstances() && (l < numInstances()))) {
			if (weights[l] < 0) {
				throw new IllegalArgumentException(
						"Weights have to be positive.");
			}
			sumProbs += weights[l];
			while ((k < numInstances()) && (probabilities[k] <= sumProbs)) {
				newData.add(instance(l));
				newData.instance(k).setWeight(1);
				k++;
			}
			l++;
		}
		return newData;
	}

	/**
	 * Sets the class attribute.
	 * 
	 * @param att
	 *            attribute to be the class
	 */
	public void setClass(Attribute att) {

		m_ClassIndex = att.index();
	}

	/**
	 * Sets the class index of the set. If the class index is negative there is
	 * assumed to be no class. (ie. it is undefined)
	 * 
	 * @param classIndex
	 *            the new class index
	 * @exception IllegalArgumentException
	 *                if the class index is too big or < 0
	 */
	public void setClassIndex(int classIndex) {

		if (classIndex >= numAttributes()) {
			throw new IllegalArgumentException("Invalid class index: "
					+ classIndex);
		}
		m_ClassIndex = classIndex;
	}

	/**
	 * Sets the relation's name.
	 * 
	 * @param newName
	 *            the new relation name.
	 */
	public void setRelationName(/* @non_null@ */String newName) {

		m_RelationName = newName;
	}

	/**
	 * Sorts the instances based on an attribute. For numeric attributes,
	 * instances are sorted in ascending order. For nominal attributes,
	 * instances are sorted based on the attribute label ordering specified in
	 * the header. Instances with missing values for the attribute are placed at
	 * the end of the dataset.
	 * 
	 * @param attIndex
	 *            the attribute's index
	 */
	public void sort(int attIndex) {

		int i, j;

		// move all instances with missing values to end
		j = numInstances() - 1;
		i = 0;
		while (i <= j) {
			if (instance(j).isMissing(attIndex)) {
				j--;
			} else {
				if (instance(i).isMissing(attIndex)) {
					swap(i, j);
					j--;
				}
				i++;
			}
		}
		quickSort(attIndex, 0, j);
	}

	/**
	 * Sorts the instances based on an attribute. For numeric attributes,
	 * instances are sorted into ascending order. For nominal attributes,
	 * instances are sorted based on the attribute label ordering specified in
	 * the header. Instances with missing values for the attribute are placed at
	 * the end of the dataset.
	 * 
	 * @param att
	 *            the attribute
	 */
	public void sort(Attribute att) {

		sort(att.index());
	}

	/**
	 * Stratifies a set of instances according to its class values if the class
	 * attribute is nominal (so that afterwards a stratified cross-validation
	 * can be performed).
	 * 
	 * @param numFolds
	 *            the number of folds in the cross-validation
	 * @exception UnassignedClassException
	 *                if the class is not set
	 */
	public void stratify(int numFolds) {

		if (numFolds <= 0) {
			throw new IllegalArgumentException(
					"Number of folds must be greater than 1");
		}
		if (m_ClassIndex < 0) {
			throw new UnassignedClassException(
					"Class index is negative (not set)!");
		}
		if (classAttribute().isNominal()) {

			// sort by class
			int index = 1;
			while (index < numInstances()) {
				Instance instance1 = instance(index - 1);
				for (int j = index; j < numInstances(); j++) {
					Instance instance2 = instance(j);
					if ((instance1.classValue() == instance2.classValue())
							|| (instance1.classIsMissing() && instance2
									.classIsMissing())) {
						swap(index, j);
						index++;
					}
				}
				index++;
			}
			stratStep(numFolds);
		}
	}

	/**
	 * Computes the sum of all the instances' weights.
	 * 
	 * @return the sum of all the instances' weights as a double
	 */
	public/* @pure@ */double sumOfWeights() {

		double sum = 0;

		for (int i = 0; i < numInstances(); i++) {
			sum += instance(i).weight();
		}
		return sum;
	}

	/**
	 * Creates the test set for one fold of a cross-validation on the dataset.
	 * 
	 * @param numFolds
	 *            the number of folds in the cross-validation. Must be greater
	 *            than 1.
	 * @param numFold
	 *            0 for the first fold, 1 for the second, ...
	 * @return the test set as a set of weighted instances
	 * @exception IllegalArgumentException
	 *                if the number of folds is less than 2 or greater than the
	 *                number of instances.
	 */
	// @ requires 2 <= numFolds && numFolds < numInstances();
	// @ requires 0 <= numFold && numFold < numFolds;
	public Instances testCV(int numFolds, int numFold) {

		int numInstForFold, first, offset;
		Instances test;

		if (numFolds < 2) {
			throw new IllegalArgumentException(
					"Number of folds must be at least 2!");
		}
		if (numFolds > numInstances()) {
			throw new IllegalArgumentException(
					"Can't have more folds than instances!");
		}
		numInstForFold = numInstances() / numFolds;
		if (numFold < numInstances() % numFolds) {
			numInstForFold++;
			offset = numFold;
		} else
			offset = numInstances() % numFolds;
		test = new Instances(this, numInstForFold);
		first = numFold * (numInstances() / numFolds) + offset;
		copyInstances(first, test, numInstForFold);
		return test;
	}

	/**
	 * Returns the dataset as a string in ARFF format. Strings are quoted if
	 * they contain whitespace characters, or if they are a question mark.
	 * 
	 * @return the dataset in ARFF format as a string
	 */
	public String toString() {//**aquí es donde se configura el header y donde quité los espacios**

		StringBuffer text = new StringBuffer();

		text.append(ARFF_RELATION).append(" ").append(
				Utils.quote(m_RelationName)).append("\n");
		for (int i = 0; i < numAttributes(); i++) {
			text.append(attribute(i)).append("\n");
		}
		text.append(ARFF_DATA).append("\n");

		text.append(stringWithoutHeader());
		return text.toString();
	}

	/**
	 * Returns the instances in the dataset as a string in ARFF format. Strings
	 * are quoted if they contain whitespace characters, or if they are a
	 * question mark.
	 * 
	 * @return the dataset in ARFF format as a string
	 */
	protected String stringWithoutHeader() {

		StringBuffer text = new StringBuffer();

		for (int i = 0; i < numInstances(); i++) {
			text.append(instance(i));
			if (i < numInstances() - 1) {
				text.append('\n');
			}
		}
		return text.toString();
	}

	/**
	 * Creates the training set for one fold of a cross-validation on the
	 * dataset.
	 * 
	 * @param numFolds
	 *            the number of folds in the cross-validation. Must be greater
	 *            than 1.
	 * @param numFold
	 *            0 for the first fold, 1 for the second, ...
	 * @return the training set
	 * @exception IllegalArgumentException
	 *                if the number of folds is less than 2 or greater than the
	 *                number of instances.
	 */
	// @ requires 2 <= numFolds && numFolds < numInstances();
	// @ requires 0 <= numFold && numFold < numFolds;
	public Instances trainCV(int numFolds, int numFold) {

		int numInstForFold, first, offset;
		Instances train;

		if (numFolds < 2) {
			throw new IllegalArgumentException(
					"Number of folds must be at least 2!");
		}
		if (numFolds > numInstances()) {
			throw new IllegalArgumentException(
					"Can't have more folds than instances!");
		}
		numInstForFold = numInstances() / numFolds;
		if (numFold < numInstances() % numFolds) {
			numInstForFold++;
			offset = numFold;
		} else
			offset = numInstances() % numFolds;
		train = new Instances(this, numInstances() - numInstForFold);
		first = numFold * (numInstances() / numFolds) + offset;
		copyInstances(0, train, first);
		copyInstances(first + numInstForFold, train, numInstances() - first
				- numInstForFold);

		return train;
	}

	/**
	 * Creates the training set for one fold of a cross-validation on the
	 * dataset. The data is subsequently randomized based on the given random
	 * number generator.
	 * 
	 * @param numFolds
	 *            the number of folds in the cross-validation. Must be greater
	 *            than 1.
	 * @param numFold
	 *            0 for the first fold, 1 for the second, ...
	 * @param random
	 *            the random number generator
	 * @return the training set
	 * @exception IllegalArgumentException
	 *                if the number of folds is less than 2 or greater than the
	 *                number of instances.
	 */
	// @ requires 2 <= numFolds && numFolds < numInstances();
	// @ requires 0 <= numFold && numFold < numFolds;
	public Instances trainCV(int numFolds, int numFold, Random random) {

		Instances train = trainCV(numFolds, numFold);
		train.randomize(random);
		return train;
	}

	/**
	 * Computes the variance for a numeric attribute.
	 * 
	 * @param attIndex
	 *            the numeric attribute
	 * @return the variance if the attribute is numeric
	 * @exception IllegalArgumentException
	 *                if the attribute is not numeric
	 */
	public/* @pure@ */double variance(int attIndex) {

		double sum = 0, sumSquared = 0, sumOfWeights = 0;

		if (!attribute(attIndex).isReal()) {
			throw new IllegalArgumentException(
					"Can't compute variance because attribute is "
							+ "not real!");
		}
		for (int i = 0; i < numInstances(); i++) {
			if (!instance(i).isMissing(attIndex)) {
				sum += instance(i).weight() * instance(i).value(attIndex);
				sumSquared += instance(i).weight()
						* instance(i).value(attIndex)
						* instance(i).value(attIndex);
				sumOfWeights += instance(i).weight();
			}
		}
		if (sumOfWeights <= 1) {
			return 0;
		}
		double result = (sumSquared - (sum * sum / sumOfWeights))
				/ (sumOfWeights - 1);

		// We don't like negative variance
		if (result < 0) {
			return 0;
		} else {
			return result;
		}
	}

	/**
	 * Computes the variance for a numeric attribute.
	 * 
	 * @param att
	 *            the numeric attribute
	 * @return the variance if the attribute is numeric
	 * @exception IllegalArgumentException
	 *                if the attribute is not numeric
	 */
	public/* @pure@ */double variance(Attribute att) {

		return variance(att.index());
	}

	/**
	 * Calculates summary statistics on the values that appear in this set of
	 * instances for a specified attribute.
	 * 
	 * @param index
	 *            the index of the attribute to summarize.
	 * @return an AttributeStats object with it's fields calculated.
	 */
	// @ requires 0 <= index && index < numAttributes();
	public AttributeStats attributeStats(int index) {

		AttributeStats result = new AttributeStats();
		if (attribute(index).isNominal()) {
			result.nominalCounts = new int[attribute(index).numValues()];
		}
		if (attribute(index).isReal()) {
			result.realStats = new Stats();
		}
		result.totalCount = numInstances();

		double[] attVals = attributeToDoubleArray(index);
		int[] sorted = Utils.sort(attVals);
		int currentCount = 0;
		double prev = Instance.missingValue();
		for (int j = 0; j < numInstances(); j++) {
			Instance current = instance(sorted[j]);
			if (current.isMissing(index)) {
				result.missingCount = numInstances() - j;
				break;
			}
			if (current.value(index) == prev) {
				currentCount++;
			} else {
				result.addDistinct(prev, currentCount);
				currentCount = 1;
				prev = current.value(index);
			}
		}
		result.addDistinct(prev, currentCount);
		result.distinctCount--; // So we don't count "missing" as a value
		return result;
	}

	/**
	 * Gets the value of all instances in this dataset for a particular
	 * attribute. Useful in conjunction with Utils.sort to allow iterating
	 * through the dataset in sorted order for some attribute.
	 * 
	 * @param index
	 *            the index of the attribute.
	 * @return an array containing the value of the desired attribute for each
	 *         instance in the dataset.
	 */
	// @ requires 0 <= index && index < numAttributes();
	public/* @pure@ */double[] attributeToDoubleArray(int index) {

		double[] result = new double[numInstances()];
		for (int i = 0; i < result.length; i++) {
			result[i] = instance(i).value(index);
		}
		return result;
	}

	/**
	 * Generates a string summarizing the set of instances. Gives a breakdown
	 * for each attribute indicating the number of missing/discrete/unique
	 * values and other information.
	 * 
	 * @return a string summarizing the dataset
	 */
	public String toSummaryString() {

		StringBuffer result = new StringBuffer();
		result.append("Relation Name:  ").append(relationName()).append('\n');
		result.append("Num Instances:  ").append(numInstances()).append('\n');
		result.append("Num Attributes: ").append(numAttributes()).append('\n');
		result.append('\n');

		result.append(Utils.padLeft("", 5)).append(Utils.padRight("Name", 25));
		result.append(Utils.padLeft("Type", 5)).append(Utils.padLeft("Nom", 5));
		result.append(Utils.padLeft("Int", 5)).append(Utils.padLeft("Real", 5));
		result.append(Utils.padLeft("Missing", 12));
		result.append(Utils.padLeft("Unique", 12));
		result.append(Utils.padLeft("Dist", 6)).append('\n');
		for (int i = 0; i < numAttributes(); i++) {
			Attribute a = attribute(i);
			AttributeStats as = attributeStats(i);
			result.append(Utils.padLeft("" + (i + 1), 4)).append(' ');
			result.append(Utils.padRight(a.name(), 25)).append(' ');
			long percent;
			switch (a.type()) {
			case Attribute.NOMINAL:
				result.append(Utils.padLeft("Nom", 4)).append(' ');
				percent = Math.round(100.0 * as.intCount / as.totalCount);
				result.append(Utils.padLeft("" + percent, 3)).append("% ");
				result.append(Utils.padLeft("" + 0, 3)).append("% ");
				percent = Math.round(100.0 * as.realCount / as.totalCount);
				result.append(Utils.padLeft("" + percent, 3)).append("% ");
				break;
			case Attribute.REAL:
				result.append(Utils.padLeft("Num", 4)).append(' ');
				result.append(Utils.padLeft("" + 0, 3)).append("% ");
				percent = Math.round(100.0 * as.intCount / as.totalCount);
				result.append(Utils.padLeft("" + percent, 3)).append("% ");
				percent = Math.round(100.0 * as.realCount / as.totalCount);
				result.append(Utils.padLeft("" + percent, 3)).append("% ");
				break;
			case Attribute.DATE:
				result.append(Utils.padLeft("Dat", 4)).append(' ');
				result.append(Utils.padLeft("" + 0, 3)).append("% ");
				percent = Math.round(100.0 * as.intCount / as.totalCount);
				result.append(Utils.padLeft("" + percent, 3)).append("% ");
				percent = Math.round(100.0 * as.realCount / as.totalCount);
				result.append(Utils.padLeft("" + percent, 3)).append("% ");
				break;
			case Attribute.STRING:
				result.append(Utils.padLeft("Str", 4)).append(' ');
				percent = Math.round(100.0 * as.intCount / as.totalCount);
				result.append(Utils.padLeft("" + percent, 3)).append("% ");
				result.append(Utils.padLeft("" + 0, 3)).append("% ");
				percent = Math.round(100.0 * as.realCount / as.totalCount);
				result.append(Utils.padLeft("" + percent, 3)).append("% ");
				break;
			case Attribute.RELATIONAL:
				result.append(Utils.padLeft("Rel", 4)).append(' ');
				percent = Math.round(100.0 * as.intCount / as.totalCount);
				result.append(Utils.padLeft("" + percent, 3)).append("% ");
				result.append(Utils.padLeft("" + 0, 3)).append("% ");
				percent = Math.round(100.0 * as.realCount / as.totalCount);
				result.append(Utils.padLeft("" + percent, 3)).append("% ");
				break;
			default:
				result.append(Utils.padLeft("???", 4)).append(' ');
				result.append(Utils.padLeft("" + 0, 3)).append("% ");
				percent = Math.round(100.0 * as.intCount / as.totalCount);
				result.append(Utils.padLeft("" + percent, 3)).append("% ");
				percent = Math.round(100.0 * as.realCount / as.totalCount);
				result.append(Utils.padLeft("" + percent, 3)).append("% ");
				break;
			}
			result.append(Utils.padLeft("" + as.missingCount, 5)).append(" /");
			percent = Math.round(100.0 * as.missingCount / as.totalCount);
			result.append(Utils.padLeft("" + percent, 3)).append("% ");
			result.append(Utils.padLeft("" + as.uniqueCount, 5)).append(" /");
			percent = Math.round(100.0 * as.uniqueCount / as.totalCount);
			result.append(Utils.padLeft("" + percent, 3)).append("% ");
			result.append(Utils.padLeft("" + as.distinctCount, 5)).append(' ');
			result.append('\n');
		}
		return result.toString();
	}

	/**
	 * Reads a single instance using the tokenizer and appends it to the
	 * dataset. Automatically expands the dataset if it is not large enough to
	 * hold the instance.
	 * 
	 * @param tokenizer
	 *            the tokenizer to be used
	 * @param flag
	 *            if method should test for carriage return after each instance
	 * @return false if end of file has been reached
	 * @exception IOException
	 *                if the information is not read successfully
	 */
	protected boolean getInstance(StreamTokenizer tokenizer, boolean flag)
			throws IOException {

		// Check if any attributes have been declared.
		if (m_Attributes.size() == 0) {
			errms(tokenizer, "no header information available");
		}

		// Check if end of file reached.
		getFirstToken(tokenizer);
		if (tokenizer.ttype == StreamTokenizer.TT_EOF) {
			return false;
		}

		// Parse instance
		if (tokenizer.ttype == '{') {
			return getInstanceSparse(tokenizer, flag);
		} else {
			return getInstanceFull(tokenizer, flag);
		}
	}

	/**
	 * Reads a single instance using the tokenizer and appends it to the
	 * dataset. Automatically expands the dataset if it is not large enough to
	 * hold the instance.
	 * 
	 * @param tokenizer
	 *            the tokenizer to be used
	 * @param flag
	 *            if method should test for carriage return after each instance
	 * @return false if end of file has been reached
	 * @exception IOException
	 *                if the information is not read successfully
	 */
	protected boolean getInstanceSparse(StreamTokenizer tokenizer, boolean flag)
			throws IOException {

		int valIndex, numValues = 0, maxIndex = -1;

		// Get values
		do {

			// Get index
			getIndex(tokenizer);
			if (tokenizer.ttype == '}') {
				break;
			}

			// Is index valid?
			try {
				m_IndicesBuffer[numValues] = Integer.valueOf(tokenizer.sval)
						.intValue();
			} catch (NumberFormatException e) {
				errms(tokenizer, "index number expected");
			}
			if (m_IndicesBuffer[numValues] <= maxIndex) {
				errms(tokenizer, "indices have to be ordered");
			}
			if ((m_IndicesBuffer[numValues] < 0)
					|| (m_IndicesBuffer[numValues] >= numAttributes())) {
				errms(tokenizer, "index out of bounds");
			}
			maxIndex = m_IndicesBuffer[numValues];

			// Get value;
			getNextToken(tokenizer);

			// Check if value is missing.
			if (tokenizer.ttype == '?') {
				m_ValueBuffer[numValues] = Instance.missingValue();
			} else {

				// Check if token is valid.
				if (tokenizer.ttype != StreamTokenizer.TT_WORD) {
					errms(tokenizer, "not a valid value");
				}
				switch (attribute(m_IndicesBuffer[numValues]).type()) {
				case Attribute.NOMINAL:
					// Check if value appears in header.
					valIndex = attribute(m_IndicesBuffer[numValues])
							.indexOfValue(tokenizer.sval);
					if (valIndex == -1) {
						errms(tokenizer, "nominal value not declared in header");
					}
					m_ValueBuffer[numValues] = (double) valIndex;
					break;
				case Attribute.REAL:
					// Check if value is really a number.
					try {
						m_ValueBuffer[numValues] = Double.valueOf(
								tokenizer.sval).doubleValue();
					} catch (NumberFormatException e) {
						errms(tokenizer, "number expected");
					}
					break;
				case Attribute.STRING:
					m_ValueBuffer[numValues] = attribute(
							m_IndicesBuffer[numValues]).addStringValue(
							tokenizer.sval);
					break;
				case Attribute.DATE:
					try {
						m_ValueBuffer[numValues] = attribute(
								m_IndicesBuffer[numValues]).parseDate(
								tokenizer.sval);
					} catch (ParseException e) {
						errms(tokenizer, "unparseable date: " + tokenizer.sval);
					}
					break;
				case Attribute.RELATIONAL:
					StringReader reader = new StringReader(tokenizer.sval);
					StreamTokenizer innerTokenizer = new StreamTokenizer(reader);
					initTokenizer(innerTokenizer);
					Instances data = new Instances(attribute(
							m_IndicesBuffer[numValues]).relation(), 100);

					// Allocate buffers in case sparse instances have to be read
					data.m_ValueBuffer = new double[data.numAttributes()];
					data.m_IndicesBuffer = new int[data.numAttributes()];

					while (data.getInstance(innerTokenizer, true)) {
					}
					;
					data.compactify();
					m_ValueBuffer[numValues] = attribute(
							m_IndicesBuffer[numValues]).addRelation(data);
					break;
				default:
					errms(tokenizer, "unknown attribute type in column "
							+ m_IndicesBuffer[numValues]);
				}
			}
			numValues++;
		} while (true);
		if (flag) {
			getLastToken(tokenizer, true);
		}

		// Add instance to dataset
		double[] tempValues = new double[numValues];
		int[] tempIndices = new int[numValues];
		System.arraycopy(m_ValueBuffer, 0, tempValues, 0, numValues);
		System.arraycopy(m_IndicesBuffer, 0, tempIndices, 0, numValues);
		add(new SparseInstance(1, tempValues, tempIndices, numAttributes()));
		return true;
	}

	/**
	 * Reads a single instance using the tokenizer and appends it to the
	 * dataset. Automatically expands the dataset if it is not large enough to
	 * hold the instance.
	 * 
	 * @param tokenizer
	 *            the tokenizer to be used
	 * @param flag
	 *            if method should test for carriage return after each instance
	 * @return false if end of file has been reached
	 * @exception IOException
	 *                if the information is not read successfully
	 */
	protected boolean getInstanceFull(StreamTokenizer tokenizer, boolean flag)
			throws IOException {

		double[] instance = new double[numAttributes()];
		int index;

		// Get values for all attributes.
		for (int i = 0; i < numAttributes(); i++) {

			// Get next token
			if (i > 0) {
				getNextToken(tokenizer);
			}

			// Check if value is missing.
			if (tokenizer.ttype == '?') {
				instance[i] = Instance.missingValue();
			} else {

				// Check if token is valid.
				if (tokenizer.ttype != StreamTokenizer.TT_WORD) {
					errms(tokenizer, "not a valid value");
				}
				switch (attribute(i).type()) {
				case Attribute.NOMINAL:
					// Check if value appears in header.
					index = attribute(i).indexOfValue(tokenizer.sval);
					if (index == -1) {
						errms(tokenizer, "nominal value not declared in header");
					}
					instance[i] = (double) index;
					break;
				case Attribute.REAL:
					// Check if value is really a number.
					try {
						instance[i] = Double.valueOf(tokenizer.sval)
								.doubleValue();
					} catch (NumberFormatException e) {
						errms(tokenizer, "number expected");
					}
					break;
				case Attribute.STRING:
					instance[i] = attribute(i).addStringValue(tokenizer.sval);
					break;
				case Attribute.DATE:
					try {
						instance[i] = attribute(i).parseDate(tokenizer.sval);
					} catch (ParseException e) {
						errms(tokenizer, "unparseable date: " + tokenizer.sval);
					}
					break;
				case Attribute.RELATIONAL:
					StringReader reader = new StringReader(tokenizer.sval);
					StreamTokenizer innerTokenizer = new StreamTokenizer(reader);
					initTokenizer(innerTokenizer);
					Instances data = new Instances(attribute(i).relation(), 100);

					// Allocate buffers in case sparse instances have to be read
					data.m_ValueBuffer = new double[data.numAttributes()];
					data.m_IndicesBuffer = new int[data.numAttributes()];

					while (data.getInstance(innerTokenizer, true)) {
					}
					;
					data.compactify();
					instance[i] = attribute(i).addRelation(data);
					break;
				default:
					errms(tokenizer, "unknown attribute type in column " + i);
				}
			}
		}
		if (flag) {
			getLastToken(tokenizer, true);
		}

		// Add instance to dataset
		add(new Instance(1, instance));
		return true;
	}

	/**
	 * Reads and stores header of an ARFF file.
	 * 
	 * @param tokenizer
	 *            the stream tokenizer
	 * @exception IOException
	 *                if the information is not read successfully
	 */
	protected void readHeader(StreamTokenizer tokenizer) throws IOException {

		// Get name of relation.
		getFirstToken(tokenizer);
		if (tokenizer.ttype == StreamTokenizer.TT_EOF) {
			errms(tokenizer, "premature end of file");
		}
		if (ARFF_RELATION.equalsIgnoreCase(tokenizer.sval)) {
			getNextToken(tokenizer);
			m_RelationName = tokenizer.sval;
			getLastToken(tokenizer, false);
		} else {
			errms(tokenizer, "keyword " + ARFF_RELATION + " expected");
		}

		// Create vectors to hold information temporarily.
		m_Attributes = new FastVector();

		// Get attribute declarations.
		getFirstToken(tokenizer);
		if (tokenizer.ttype == StreamTokenizer.TT_EOF) {
			errms(tokenizer, "premature end of file");
		}

		while (Attribute.ARFF_ATTRIBUTE.equalsIgnoreCase(tokenizer.sval)) {
			parseAttribute(tokenizer);
		}
		
		if (tokenizer.sval.indexOf("@input") > -1){
			readTillEOL(tokenizer);
			getLastToken(tokenizer, false);
			getFirstToken(tokenizer);
			readTillEOL(tokenizer);
			getLastToken(tokenizer, false);
			getFirstToken(tokenizer);
		}

		// Check if data part follows. We can't easily check for EOL.
		if (!ARFF_DATA.equalsIgnoreCase(tokenizer.sval)) {
			errms(tokenizer, "keyword " + ARFF_DATA + " expected");
		}

		// Check if any attributes have been declared.
		if (m_Attributes.size() == 0) {
			errms(tokenizer, "no attributes declared");
		}

		// Allocate buffers in case sparse instances have to be read
		m_ValueBuffer = new double[numAttributes()];
		m_IndicesBuffer = new int[numAttributes()];
	}

	/**
	 * Parses the attribute declaration.
	 * 
	 * @param tokenizer
	 *            the stream tokenizer
	 * @exception IOException
	 *                if the information is not read successfully
	 */
	protected void parseAttribute(StreamTokenizer tokenizer) throws IOException {

		String attributeName;
		FastVector attributeValues;

		// Get attribute name.
		getNextToken(tokenizer);
		attributeName = tokenizer.sval;
		getNextToken(tokenizer);

		// Check if attribute is nominal.
		if (tokenizer.ttype == StreamTokenizer.TT_WORD) {

			// Attribute is real, integer, or string.
			if (tokenizer.sval.equalsIgnoreCase(Attribute.ARFF_ATTRIBUTE_REAL)
					|| tokenizer.sval
							.equalsIgnoreCase(Attribute.ARFF_ATTRIBUTE_INTEGER)
					|| tokenizer.sval
							.equalsIgnoreCase(Attribute.ARFF_ATTRIBUTE_REAL)) {
				m_Attributes.addElement(new Attribute(attributeName,
						numAttributes()));
				readTillEOL(tokenizer);
			} else if (tokenizer.sval
					.equalsIgnoreCase(Attribute.ARFF_ATTRIBUTE_STRING)) {
				m_Attributes.addElement(new Attribute(attributeName,
						(FastVector) null, numAttributes()));
				readTillEOL(tokenizer);
			} else if (tokenizer.sval
					.equalsIgnoreCase(Attribute.ARFF_ATTRIBUTE_DATE)) {
				String format = null;
				if (tokenizer.nextToken() != StreamTokenizer.TT_EOL) {
					if ((tokenizer.ttype != StreamTokenizer.TT_WORD)
							&& (tokenizer.ttype != '\'')
							&& (tokenizer.ttype != '\"')) {
						errms(tokenizer, "not a valid date format");
					}
					format = tokenizer.sval;
					readTillEOL(tokenizer);
				} else {
					tokenizer.pushBack();
				}
				m_Attributes.addElement(new Attribute(attributeName, format,
						numAttributes()));

			} else if (tokenizer.sval
					.equalsIgnoreCase(Attribute.ARFF_ATTRIBUTE_RELATIONAL)) {
				readTillEOL(tokenizer);

				// Read attributes for subrelation
				// First, save current set of attributes
				FastVector atts = m_Attributes;
				m_Attributes = new FastVector();

				// Now, read attributes until we hit end of declaration of
				// relational value
				getFirstToken(tokenizer);
				if (tokenizer.ttype == StreamTokenizer.TT_EOF) {
					errms(tokenizer, "premature end of file");
				}
				do {
					if (Attribute.ARFF_ATTRIBUTE
							.equalsIgnoreCase(tokenizer.sval)) {
						parseAttribute(tokenizer);
					} else if (Attribute.ARFF_END_SUBRELATION
							.equalsIgnoreCase(tokenizer.sval)) {
						getNextToken(tokenizer);
						if (!attributeName.equalsIgnoreCase(tokenizer.sval)) {
							errms(tokenizer, "declaration of subrelation "
									+ attributeName + " must be terminated by "
									+ "@end " + attributeName);
						}
						break;
					} else {
						errms(tokenizer, "declaration of subrelation "
								+ attributeName + " must be terminated by "
								+ "@end " + attributeName);
					}
				} while (true);

				// Make relation and restore original set of attributes
				Instances relation = new Instances(attributeName, m_Attributes,
						0);
				m_Attributes = atts;
				m_Attributes.addElement(new Attribute(attributeName, relation,
						numAttributes()));
			} else {
				errms(tokenizer, "no valid attribute type or invalid "
						+ "enumeration");
			}
		} else {

			// Attribute is nominal.
			attributeValues = new FastVector();
			tokenizer.pushBack();

			// Get values for nominal attribute.
			if (tokenizer.nextToken() != '{') {
				errms(tokenizer, "{ expected at beginning of enumeration");
			}
			while (tokenizer.nextToken() != '}') {
				if (tokenizer.ttype == StreamTokenizer.TT_EOL) {
					errms(tokenizer, "} expected at end of enumeration");
				} else {
					attributeValues.addElement(tokenizer.sval);
				}
			}
			if (attributeValues.size() == 0) {
				errms(tokenizer, "no nominal values found");
			}
			m_Attributes.addElement(new Attribute(attributeName,
					attributeValues, numAttributes()));
		}
		getLastToken(tokenizer, false);
		getFirstToken(tokenizer);
		if (tokenizer.ttype == StreamTokenizer.TT_EOF)
			errms(tokenizer, "premature end of file");
	}

	/**
	 * Copies instances from one set to the end of another one.
	 * 
	 * @param source
	 *            the source of the instances
	 * @param from
	 *            the position of the first instance to be copied
	 * @param dest
	 *            the destination for the instances
	 * @param num
	 *            the number of instances to be copied
	 */
	// @ requires 0 <= from && from <= numInstances() - num;
	// @ requires 0 <= num;
	protected void copyInstances(int from, /* @non_null@ */Instances dest,
			int num) {

		for (int i = 0; i < num; i++) {
			dest.add(instance(from + i));
		}
	}

	/**
	 * Throws error message with line number and last token read.
	 * 
	 * @param theMsg
	 *            the error message to be thrown
	 * @param tokenizer
	 *            the stream tokenizer
	 * @throws IOExcpetion
	 *             containing the error message
	 */
	protected void errms(StreamTokenizer tokenizer, String theMsg)
			throws IOException {

		throw new IOException(theMsg + ", read " + tokenizer.toString());
	}

	/**
	 * Replaces the attribute information by a clone of itself.
	 */
	protected void freshAttributeInfo() {

		m_Attributes = (FastVector) m_Attributes.copyElements();
	}

	/**
	 * Gets next token, skipping empty lines.
	 * 
	 * @param tokenizer
	 *            the stream tokenizer
	 * @exception IOException
	 *                if reading the next token fails
	 */
	protected void getFirstToken(StreamTokenizer tokenizer) throws IOException {

		while (tokenizer.nextToken() == StreamTokenizer.TT_EOL) {
		}
		;
		if ((tokenizer.ttype == '\'') || (tokenizer.ttype == '"')) {
			tokenizer.ttype = StreamTokenizer.TT_WORD;
		} else if ((tokenizer.ttype == StreamTokenizer.TT_WORD)
				&& (tokenizer.sval.equals("?"))) {
			tokenizer.ttype = '?';
		}
	}

	/**
	 * Gets index, checking for a premature and of line.
	 * 
	 * @param tokenizer
	 *            the stream tokenizer
	 * @exception IOException
	 *                if it finds a premature end of line
	 */
	protected void getIndex(StreamTokenizer tokenizer) throws IOException {

		if (tokenizer.nextToken() == StreamTokenizer.TT_EOL) {
			errms(tokenizer, "premature end of line");
		}
		if (tokenizer.ttype == StreamTokenizer.TT_EOF) {
			errms(tokenizer, "premature end of file");
		}
	}

	/**
	 * Gets token and checks if its end of line.
	 * 
	 * @param tokenizer
	 *            the stream tokenizer
	 * @exception IOException
	 *                if it doesn't find an end of line
	 */
	protected void getLastToken(StreamTokenizer tokenizer, boolean endOfFileOk)
			throws IOException {

		if ((tokenizer.nextToken() != StreamTokenizer.TT_EOL)
				&& ((tokenizer.ttype != StreamTokenizer.TT_EOF) || !endOfFileOk)) {
			errms(tokenizer, "end of line expected");
		}
	}

	/**
	 * Gets next token, checking for a premature and of line.
	 * 
	 * @param tokenizer
	 *            the stream tokenizer
	 * @exception IOException
	 *                if it finds a premature end of line
	 */
	protected void getNextToken(StreamTokenizer tokenizer) throws IOException {

		if (tokenizer.nextToken() == StreamTokenizer.TT_EOL) {
			errms(tokenizer, "premature end of line");
		}
		if (tokenizer.ttype == StreamTokenizer.TT_EOF) {
			errms(tokenizer, "premature end of file");
		} else if ((tokenizer.ttype == '\'') || (tokenizer.ttype == '"')) {
			tokenizer.ttype = StreamTokenizer.TT_WORD;
		} else if ((tokenizer.ttype == StreamTokenizer.TT_WORD)
				&& (tokenizer.sval.equals("?"))) {
			tokenizer.ttype = '?';
		}
	}

	/**
	 * Initializes the StreamTokenizer used for reading the ARFF file.
	 * 
	 * @param tokenizer
	 *            the stream tokenizer
	 */
	protected void initTokenizer(StreamTokenizer tokenizer) {

		tokenizer.resetSyntax();
		tokenizer.whitespaceChars(0, ' ');
		tokenizer.wordChars(' ' + 1, '\u00FF');
		tokenizer.whitespaceChars(',', ',');
		tokenizer.commentChar('%');
		tokenizer.quoteChar('"');
		tokenizer.quoteChar('\'');
		tokenizer.ordinaryChar('{');
		tokenizer.ordinaryChar('}');
		tokenizer.eolIsSignificant(true);
	}

	/**
	 * Returns string including all instances, their weights and their indices
	 * in the original dataset.
	 * 
	 * @return description of instance and its weight as a string
	 */
	protected/* @pure@ */String instancesAndWeights() {

		StringBuffer text = new StringBuffer();

		for (int i = 0; i < numInstances(); i++) {
			text.append(instance(i) + " " + instance(i).weight());
			if (i < numInstances() - 1) {
				text.append("\n");
			}
		}
		return text.toString();
	}

	/**
	 * Partitions the instances around a pivot. Used by quicksort and
	 * kthSmallestValue.
	 * 
	 * @param attIndex
	 *            the attribute's index
	 * @param left
	 *            the first index of the subset
	 * @param right
	 *            the last index of the subset
	 * 
	 * @return the index of the middle element
	 */
	// @ requires 0 <= attIndex && attIndex < numAttributes();
	// @ requires 0 <= left && left <= right && right < numInstances();
	protected int partition(int attIndex, int l, int r) {

		double pivot = instance((l + r) / 2).value(attIndex);

		while (l < r) {
			while ((instance(l).value(attIndex) < pivot) && (l < r)) {
				l++;
			}
			while ((instance(r).value(attIndex) > pivot) && (l < r)) {
				r--;
			}
			if (l < r) {
				swap(l, r);
				l++;
				r--;
			}
		}
		if ((l == r) && (instance(r).value(attIndex) > pivot)) {
			r--;
		}

		return r;
	}

	/**
	 * Implements quicksort according to Manber's "Introduction to Algorithms".
	 * 
	 * @param attIndex
	 *            the attribute's index
	 * @param left
	 *            the first index of the subset to be sorted
	 * @param right
	 *            the last index of the subset to be sorted
	 */
	// @ requires 0 <= attIndex && attIndex < numAttributes();
	// @ requires 0 <= first && first <= right && right < numInstances();
	protected void quickSort(int attIndex, int left, int right) {

		if (left < right) {
			int middle = partition(attIndex, left, right);
			quickSort(attIndex, left, middle);
			quickSort(attIndex, middle + 1, right);
		}
	}

	/**
	 * Reads and skips all tokens before next end of line token.
	 * 
	 * @param tokenizer
	 *            the stream tokenizer
	 */
	protected void readTillEOL(StreamTokenizer tokenizer) throws IOException {

		while (tokenizer.nextToken() != StreamTokenizer.TT_EOL) {
		}
		;
		tokenizer.pushBack();
	}

	/**
	 * Implements computation of the kth-smallest element according to Manber's
	 * "Introduction to Algorithms".
	 * 
	 * @param attIndex
	 *            the attribute's index
	 * @param left
	 *            the first index of the subset
	 * @param right
	 *            the last index of the subset
	 * @param k
	 *            the value of k
	 * 
	 * @return the index of the kth-smallest element
	 */
	// @ requires 0 <= attIndex && attIndex < numAttributes();
	// @ requires 0 <= first && first <= right && right < numInstances();
	protected int select(int attIndex, int left, int right, int k) {

		if (left == right) {
			return left;
		} else {
			int middle = partition(attIndex, left, right);
			if ((middle - left + 1) >= k) {
				return select(attIndex, left, middle, k);
			} else {
				return select(attIndex, middle + 1, right, k
						- (middle - left + 1));
			}
		}
	}

	/**
	 * Help function needed for stratification of set.
	 * 
	 * @param numFolds
	 *            the number of folds for the stratification
	 */
	protected void stratStep(int numFolds) {

		FastVector newVec = new FastVector(m_Instances.capacity());
		int start = 0, j;

		// create stratified batch
		while (newVec.size() < numInstances()) {
			j = start;
			while (j < numInstances()) {
				newVec.addElement(instance(j));
				j = j + numFolds;
			}
			start++;
		}
		m_Instances = newVec;
	}

	/**
	 * Swaps two instances in the set.
	 * 
	 * @param i
	 *            the first instance's index
	 * @param j
	 *            the second instance's index
	 */
	// @ requires 0 <= i && i < numInstances();
	// @ requires 0 <= j && j < numInstances();
	public void swap(int i, int j) {

		m_Instances.swap(i, j);
	}

	/**
	 * Merges two sets of Instances together. The resulting set will have all
	 * the attributes of the first set plus all the attributes of the second
	 * set. The number of instances in both sets must be the same.
	 * 
	 * @param first
	 *            the first set of Instances
	 * @param second
	 *            the second set of Instances
	 * @return the merged set of Instances
	 * @exception IllegalArgumentException
	 *                if the datasets are not the same size
	 */
	public static Instances mergeInstances(Instances first, Instances second) {

		if (first.numInstances() != second.numInstances()) {
			throw new IllegalArgumentException(
					"Instance sets must be of the same size");
		}

		// Create the vector of merged attributes
		FastVector newAttributes = new FastVector();
		for (int i = 0; i < first.numAttributes(); i++) {
			newAttributes.addElement(first.attribute(i));
		}
		for (int i = 0; i < second.numAttributes(); i++) {
			newAttributes.addElement(second.attribute(i));
		}

		// Create the set of Instances
		Instances merged = new Instances(first.relationName() + '_'
				+ second.relationName(), newAttributes, first.numInstances());
		// Merge each instance
		for (int i = 0; i < first.numInstances(); i++) {
			merged.add(first.instance(i).mergeInstance(second.instance(i)));
		}
		return merged;
	}

	/**
	 * Method for testing this class.
	 * 
	 * @param argv
	 *            should contain one element: the name of an ARFF file
	 */
	// @ requires argv != null;
	// @ requires argv.length == 1;
	// @ requires argv[0] != null;
	public static void test(String[] argv) {

		Instances instances, secondInstances, train, test, transformed, empty;
		Instance instance;
		Random random = new Random(2);
		Reader reader;
		int start, num;
		double newWeight;
		FastVector testAtts, testVals;
		int i, j;

		try {
			if (argv.length > 1) {
				throw (new Exception("Usage: Instances [<filename>]"));
			}

			// Creating set of instances from scratch
			testVals = new FastVector(2);
			testVals.addElement("first_value");
			testVals.addElement("second_value");
			testAtts = new FastVector(2);
			testAtts.addElement(new Attribute("nominal_attribute", testVals));
			testAtts.addElement(new Attribute("real_attribute"));
			instances = new Instances("test_set", testAtts, 10);
			instances.add(new Instance(instances.numAttributes()));
			instances.add(new Instance(instances.numAttributes()));
			instances.add(new Instance(instances.numAttributes()));
			instances.setClassIndex(0);
			System.out.println("\nSet of instances created from scratch:\n");
			System.out.println(instances);

			if (argv.length == 1) {
				String filename = argv[0];
				reader = new FileReader(filename);

				// Read first five instances and print them
				System.out.println("\nFirst five instances from file:\n");
				instances = new Instances(reader, 1);
				instances.setClassIndex(instances.numAttributes() - 1);
				i = 0;
				while ((i < 5) && (instances.readInstance(reader))) {
					i++;
				}
				System.out.println(instances);

				// Read all the instances in the file
				reader = new FileReader(filename);
				instances = new Instances(reader);

				// Make the last attribute be the class
				instances.setClassIndex(instances.numAttributes() - 1);

				// Print header and instances.
				System.out.println("\nDataset:\n");
				System.out.println(instances);
				System.out.println("\nClass index: " + instances.classIndex());
			}

			// Test basic methods based on class index.
			System.out.println("\nClass name: "
					+ instances.classAttribute().name());
			System.out.println("\nClass index: " + instances.classIndex());
			System.out.println("\nClass is nominal: "
					+ instances.classAttribute().isNominal());
			System.out.println("\nClass is real: "
					+ instances.classAttribute().isReal());
			System.out.println("\nClasses:\n");
			for (i = 0; i < instances.numClasses(); i++) {
				System.out.println(instances.classAttribute().value(i));
			}
			System.out.println("\nClass values and labels of instances:\n");
			for (i = 0; i < instances.numInstances(); i++) {
				Instance inst = instances.instance(i);
				System.out.print(inst.classValue() + "\t");
				System.out.print(inst.toString(inst.classIndex()));
				if (instances.instance(i).classIsMissing()) {
					System.out.println("\tis missing");
				} else {
					System.out.println();
				}
			}

			// Create random weights.
			System.out.println("\nCreating random weights for instances.");
			for (i = 0; i < instances.numInstances(); i++) {
				instances.instance(i).setWeight(random.nextDouble());
			}

			// Print all instances and their weights (and the sum of weights).
			System.out.println("\nInstances and their weights:\n");
			System.out.println(instances.instancesAndWeights());
			System.out.print("\nSum of weights: ");
			System.out.println(instances.sumOfWeights());

			// Insert an attribute
			secondInstances = new Instances(instances);
			Attribute testAtt = new Attribute("Inserted");
			secondInstances.insertAttributeAt(testAtt, 0);
			System.out.println("\nSet with inserted attribute:\n");
			System.out.println(secondInstances);
			System.out.println("\nClass name: "
					+ secondInstances.classAttribute().name());

			// Delete the attribute
			secondInstances.deleteAttributeAt(0);
			System.out.println("\nSet with attribute deleted:\n");
			System.out.println(secondInstances);
			System.out.println("\nClass name: "
					+ secondInstances.classAttribute().name());

			// Test if headers are equal
			System.out.println("\nHeaders equal: "
					+ instances.equalHeaders(secondInstances) + "\n");

			// Print data in internal format.
			System.out.println("\nData (internal values):\n");
			for (i = 0; i < instances.numInstances(); i++) {
				for (j = 0; j < instances.numAttributes(); j++) {
					if (instances.instance(i).isMissing(j)) {
						System.out.print("? ");
					} else {
						System.out.print(instances.instance(i).value(j) + " ");
					}
				}
				System.out.println();
			}

			// Just print header
			System.out.println("\nEmpty dataset:\n");
			empty = new Instances(instances, 0);
			System.out.println(empty);
			System.out
					.println("\nClass name: " + empty.classAttribute().name());

			// Create copy and rename an attribute and a value (if possible)
			if (empty.classAttribute().isNominal()) {
				Instances copy = new Instances(empty, 0);
				copy.renameAttribute(copy.classAttribute(), "new_name");
				copy.renameAttributeValue(copy.classAttribute(), copy
						.classAttribute().value(0), "new_val_name");
				System.out.println("\nDataset with names changed:\n" + copy);
				System.out.println("\nOriginal dataset:\n" + empty);
			}

			// Create and prints subset of instances.
			start = instances.numInstances() / 4;
			num = instances.numInstances() / 2;
			System.out.print("\nSubset of dataset: ");
			System.out.println(num + " instances from " + (start + 1)
					+ ". instance");
			secondInstances = new Instances(instances, start, num);
			System.out.println("\nClass name: "
					+ secondInstances.classAttribute().name());

			// Print all instances and their weights (and the sum of weights).
			System.out.println("\nInstances and their weights:\n");
			System.out.println(secondInstances.instancesAndWeights());
			System.out.print("\nSum of weights: ");
			System.out.println(secondInstances.sumOfWeights());

			// Create and print training and test sets for 3-fold
			// cross-validation.
			System.out.println("\nTrain and test folds for 3-fold CV:");
			if (instances.classAttribute().isNominal()) {
				instances.stratify(3);
			}
			for (j = 0; j < 3; j++) {
				train = instances.trainCV(3, j, new Random(1));
				test = instances.testCV(3, j);

				// Print all instances and their weights (and the sum of
				// weights).
				System.out.println("\nTrain: ");
				System.out.println("\nInstances and their weights:\n");
				System.out.println(train.instancesAndWeights());
				System.out.print("\nSum of weights: ");
				System.out.println(train.sumOfWeights());
				System.out.println("\nClass name: "
						+ train.classAttribute().name());
				System.out.println("\nTest: ");
				System.out.println("\nInstances and their weights:\n");
				System.out.println(test.instancesAndWeights());
				System.out.print("\nSum of weights: ");
				System.out.println(test.sumOfWeights());
				System.out.println("\nClass name: "
						+ test.classAttribute().name());
			}

			// Randomize instances and print them.
			System.out.println("\nRandomized dataset:");
			instances.randomize(random);

			// Print all instances and their weights (and the sum of weights).
			System.out.println("\nInstances and their weights:\n");
			System.out.println(instances.instancesAndWeights());
			System.out.print("\nSum of weights: ");
			System.out.println(instances.sumOfWeights());

			// Sort instances according to first attribute and
			// print them.
			System.out
					.print("\nInstances sorted according to first attribute:\n ");
			instances.sort(0);

			// Print all instances and their weights (and the sum of weights).
			System.out.println("\nInstances and their weights:\n");
			System.out.println(instances.instancesAndWeights());
			System.out.print("\nSum of weights: ");
			System.out.println(instances.sumOfWeights());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Main method for this class -- just prints a summary of a set of
	 * instances.
	 * 
	 * @param argv
	 *            should contain one element: the name of an ARFF file
	 */

}
